/*------------------------------------------------------------------------
//                          Project CommonLib                           //
------------------------------------------------------------------------*/

#include "stdafx.h"
#include "EntryPoint.h"
#include <commctrl.h>

DWORD EntryWindow::st_dwTlsIndex = TLS_OUT_OF_INDEXES;

namespace EntryWIN32
{
    static LRESULT CALLBACK StaticCBTProc(int msg, WPARAM wParam, LPARAM lParam)
    {
        //Retrieve pointer to CWin object from Thread Local Storage TLS
        TLSData* pTLSData = (TLSData*)TlsGetValue( EntryWindow::st_dwTlsIndex);
        if (pTLSData == NULL)
        {
            CmTRACE( TEXT("EntryWIN32::StaticCBTProc ... Unable to get TLS\n") );
            return 0;
        }
            
        EntryWindow* w = (EntryWindow*) pTLSData->pWin;

        //Only proceed for HCBT_CREATEWND messages
        if (msg != HCBT_CREATEWND)
            return ::CallNextHookEx(pTLSData->hCBTHook, msg, wParam, lParam);

        //Now remove the CBT hook
        ::UnhookWindowsHookEx(pTLSData->hCBTHook);
        pTLSData->hCBTHook = NULL;

        // Store the Window pointer into the per-window data area
#if defined (_MSC_VER) && _MSC_VER <= 1200
        //use non 64 bit compliant code for Visual C++ 6 and below
        ::SetWindowLong((HWND) wParam, GWL_USERDATA, (LONG)w);
#else
        //use 64 bit compliant code otherwise
        ::SetWindowLongPtr((HWND) wParam, GWLP_USERDATA, PtrToLong(w) );
#endif
        return 0;
    }

    static LRESULT CALLBACK StaticWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
#if defined (_MSC_VER) && _MSC_VER <= 1200
        //use non 64 bit compliant code for Visual C++ 6 and below
        EntryWinBase *pWinBase  = (EntryWinBase*) (LONG)::GetWindowLong(hWnd, GWL_USERDATA);
#else
        //use 64 bit compliant code otherwise
        EntryWinBase *pWinBase = (EntryWinBase*) (LONG_PTR)::GetWindowLongPtr(hWnd, GWLP_USERDATA);
#endif
        CmASSERT(NULL!=pWinBase);
        return pWinBase->WndProc(hWnd, uMsg, wParam, lParam);
    }


    void SetHook( LPVOID pWin)
    {       
        TLSData* pTLSData = (TLSData*)::TlsGetValue( EntryWindow::st_dwTlsIndex );

        //Create the CBT Hook
        HHOOK hHook = ::SetWindowsHookEx(WH_CBT, StaticCBTProc, 0, ::GetCurrentThreadId());
        if (!hHook)
            return;
        //throw CWinException(TEXT("CDialog::DoModal ... SetWindowsHookEx Failed"));

        //Store the hook and 'this' pointer in Thread Local Storage
        pTLSData->hCBTHook = hHook;
        pTLSData->pWin = pWin;
        ::TlsSetValue( EntryWindow::st_dwTlsIndex, pTLSData);
    }

    void RemoveHook()
    {
        TLSData* pTLSData = (TLSData*)::TlsGetValue( EntryWindow::st_dwTlsIndex );

        if (pTLSData->hCBTHook)
        {
            ::UnhookWindowsHookEx(pTLSData->hCBTHook);
            pTLSData->hCBTHook = NULL;
            ::TlsSetValue( EntryWindow::st_dwTlsIndex, pTLSData);
        }
    }


    POINT CenterWin(HWND hWnd)
    {
        //Determine the position of the top left corner which would
        // center the dialog on its parent window.

        RECT rcDialog;
        RECT rcParent;
        RECT rcDesktop;
        POINT CenterPos;

        //Get screen dimensions excluding task bar
        ::SystemParametersInfo(SPI_GETWORKAREA, 0, &rcDesktop, 0);
        int iWidth = rcDesktop.right;
        int iHeight = rcDesktop.bottom;

        //Get the dialog dimensions
        ::GetWindowRect(hWnd, &rcDialog);

        //Get the parent window dimensions (parent could be the desktop)
        HWND hParent = ::GetParent(hWnd);
        if (hParent != NULL) ::GetWindowRect(hParent, &rcParent);
        else rcParent = rcDesktop;

        //Calculate point to center the dialog on the parent window
        int x = rcParent.left + ((rcParent.right - rcParent.left) - (rcDialog.right - rcDialog.left))/2;
        int y = rcParent.top + ((rcParent.bottom - rcParent.top) - (rcDialog.bottom - rcDialog.top))/2;

        //Keep the dialog wholly on the desktop
        if (x < 0) x = 0;
        if (y < 0) y = 0;
        if (x > iWidth - (rcDialog.right - rcDialog.left))
            x = iWidth - (rcDialog.right - rcDialog.left);
        if (y > iHeight - (rcDialog.bottom - rcDialog.top))
            y = iHeight - (rcDialog.bottom - rcDialog.top);

        CenterPos.x = x;
        CenterPos.y = y;
        return CenterPos;
    }     


    bool RegisterMainWindowClass(pfMsgProc fWinMsgProc, LPCTSTR strProjectName)
    {
        //CmASSERT_MSG( NULL!=EntryWindow::GetInstance(), TEXT("Didn't Create window instance") );
        //CmASSERT( NULL!=strProjectName && "Window Projectname is NULL" );
    
        WNDCLASSEX wcx;
        wcx.cbSize = sizeof(WNDCLASSEX);
        wcx.style = 0; //Caution: CS_HREDRAW or CS_VREDRAW styles often introduce flicker.
        wcx.lpfnWndProc = (NULL!=fWinMsgProc) ? fWinMsgProc:StaticWindowProc;
        wcx.cbClsExtra = 0;
        wcx.cbWndExtra = 0;
        wcx.hInstance = EntryWindow::GetInstance();
        wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
        wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
        wcx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
        wcx.lpszMenuName =  NULL;
        wcx.lpszClassName = strProjectName;
        wcx.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

        return ( 0!=::RegisterClassEx(&wcx) );
    }
}



//////////////////////////////////////////////////////////////////////////
// EntryWinBase
//////////////////////////////////////////////////////////////////////////

HWND EntryWindow::ms_hWnd=NULL;
HINSTANCE EntryWindow::ms_hInstance =NULL;

EntryWinBase::EntryWinBase()
:CriticalObj()
{    
}
EntryWinBase::~EntryWinBase()
{
    Destroy();
}

LRESULT EntryWinBase::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_PAINT:
        {
            ::PAINTSTRUCT ps;
            //HDC hDC = ::BeginPaint(hWnd, &ps);
            ::BeginPaint(hWnd, &ps);
            //OnPaint(hDC);
            ::EndPaint(hWnd, &ps);
            return 0;
        }
    case WM_COMMAND:
        //OnCommand(wParam, lParam);
        break;
    case WM_NOTIFY:
        //return OnNotify(wParam, lParam);
        break;

        // An example of how to end the application when the window closes
        //  If needed, put this in the class you inherit from CWin
        	case WM_DESTROY:
        		//Post the WM_QUIT message to terminate the application.
        		::PostQuitMessage(0);
        		return 0;
        
    }
    // Now hand all messages to the default process
    return (::DefWindowProc(hWnd, uMsg, wParam, lParam));
}

HWND EntryWinBase::InitCreate(DWORD dwExStyle, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, 
                            RECT rSize, HWND hParent, pfMsgProc fWinMsgProc)
{
    if (::IsWindow(m_hWnd))
    {
        CmTRACE( TEXT("CWin::CreateEx ... Window already exists\n") );
        return NULL;
    }

    CmASSERT( NULL!=EntryWindow::GetInstance() );    

    if(NULL==lpszClassName)
        lpszClassName = TEXT("DEFAULT_NAME");

    WNDCLASSEX WndClassX;
    if (!::GetClassInfoEx( EntryWindow::GetInstance(), lpszClassName, &WndClassX))    
    {
        if( !EntryWIN32::RegisterMainWindowClass(fWinMsgProc, lpszClassName) )
            return NULL;
    }
        
    int x = rSize.left;
    int y = rSize.top;
    int nWidth = rSize.right - rSize.left;
    int nHeight = rSize.bottom - rSize.top;
    return OnCreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle, x, y, nWidth, nHeight, hParent);
}


HWND EntryWinBase::OnCreateEx(DWORD dwExStyle, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hParent)
{
    EntryWIN32::SetHook(this);
    m_hWnd = ::CreateWindowEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle, x, y, nWidth, nHeight, hParent, NULL, EntryWindow::GetInstance(), NULL);
    EntryWIN32::RemoveHook();

    if (!m_hWnd)
        return NULL;

    m_hWndParent = hParent;
    
    ShowWindow(m_hWnd, SW_SHOW);
    return m_hWnd;
}

void EntryWinBase::Destroy()
{
    if (::IsWindow(m_hWnd))
        ::DestroyWindow(m_hWnd);

    m_hWnd = NULL;
    m_hWndParent = NULL;        
}

//////////////////////////////////////////////////////////////////////////
// EntryWindow
//////////////////////////////////////////////////////////////////////////

EntryWindow::EntryWindow()
:m_bRun(TRUE)
{

}

EntryWindow::~EntryWindow()
{
    if (::IsWindow(ms_hWnd))
        ::DestroyWindow(ms_hWnd);

    ms_hWnd = NULL;
    //m_hWndParent = NULL;

    DestroyTls();
    
}

//BOOL EntryWindow::Initiate(const DWORD& dwX, const DWORD dwY)
//{
//    dwX, dwY;
//    return FALSE;
//}

BOOL EntryWindow::CreateTls()
{
    if( TLS_OUT_OF_INDEXES!=st_dwTlsIndex)
        return FALSE;

    st_dwTlsIndex = ::TlsAlloc();
    if (st_dwTlsIndex == TLS_OUT_OF_INDEXES)
        return FALSE; //throw CWinException(TEXT("CApp::CApp  Failed to allocate TLS Index"));

    if ((TLSData*)::TlsGetValue(st_dwTlsIndex) == NULL)
        ::TlsSetValue(st_dwTlsIndex, &m_TLSData);

    return TRUE;
}

void EntryWindow::DestroyTls()
{
    if (st_dwTlsIndex != TLS_OUT_OF_INDEXES)
    {
        ::TlsSetValue( st_dwTlsIndex, NULL);
        ::TlsFree(st_dwTlsIndex);
        st_dwTlsIndex = TLS_OUT_OF_INDEXES;
    }
}

BOOL EntryWindow::Initiate(const int& startX, const int& startY, const int& nWidth, const int& nHeight, HINSTANCE hInstance, const TCHAR* strProjectName, pfMsgProc fWinMsgProc)
{   
    CreateTls();

    lstrcpyn( m_projectName, strProjectName, eMAX_PROJECT_NAME);    
    EntryWindow::ms_hInstance = hInstance;    

    if( !EntryWIN32::RegisterMainWindowClass(fWinMsgProc, m_projectName) )
        return FALSE;

    CreateEx(startX, startY, nWidth, nHeight);

    //::ShowWindow( EntryWindow::GetHandle(), SW_SHOW);

    return OnInit( nWidth, nHeight);
}

void EntryWindow::Run()
{
    while(m_bRun)
    {
		CmTime::Sleep(1);
		if(!OnUpdate())
			return;

        if(!ProcessWinMsg())
            return;

    }
}

void EntryWindow::Terminate()
{
	m_bRun=FALSE;
    OnTerminate();
}    

HWND EntryWindow::GetHandle()
{
    return ms_hWnd;
}

HINSTANCE EntryWindow::GetInstance()
{
	if(!ms_hInstance)
	{
		ms_hInstance = GetModuleHandle(NULL);
	}
    return ms_hInstance;
}

BOOL EntryWindow::ProcessWinMsg()
{
    MSG msg;
	ZeroMemory( &msg, sizeof( msg ) );
    //if (::PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
	if (::PeekMessage(&msg, GetHandle(), 0U, 0U, PM_REMOVE))
    {
        if (msg.message == WM_QUIT)
            return FALSE;
        else
        {
            ::TranslateMessage(&msg);
            ::DispatchMessage(&msg);
        }
    }
    return TRUE;
}



BOOL EntryWindow::CreateEx(const int& startX, const int& startY,const int& nWidth, const int& nHeight)
{
    EntryWIN32::SetHook(this);

    ms_hWnd = CreateWindow( m_projectName, m_projectName, WS_OVERLAPPEDWINDOW, startX, startY, 
        nWidth, nHeight, NULL, NULL, ms_hInstance, NULL);

    EntryWIN32::RemoveHook();

    if(!ms_hWnd)
    {
        CmTRACE( TEXT("ERR: hwnd is NULL err[%d]"), GetLastError());
        return FALSE;
    }
    
    return OnCreate(startX, startY, nWidth, nHeight);    
}

BOOL EntryWindow::OnCreate(const int& startX, const int& startY,const int& nWidth, const int& nHeight)
{
    RECT windowRect;
    RECT clientRect;
    GetWindowRect(ms_hWnd, &windowRect);
    GetClientRect(ms_hWnd, &clientRect);

    UINT32 marginY = (windowRect.bottom - windowRect.top) - (clientRect.bottom - clientRect.top);
    UINT32 marginX = (clientRect.left - windowRect.left) + (windowRect.right - clientRect.right);

    SetWindowPos(ms_hWnd, HWND_TOP, startX, startY, nWidth + marginX , nHeight+ marginY, 0);

    return TRUE;
}

//////////////////////////////////////////////////////////////////////////
// EntryDialog
//////////////////////////////////////////////////////////////////////////

EntryDialog::EntryDialog()
:m_hWnd(NULL), m_bDoModal(FALSE)
{    
    ::InitCommonControls();
}
//EntryDialog(UINT nResID, HWND hWndParent = NULL);
//EntryDialog(LPCTSTR lpszResName, HWND hWndParent = NULL);
EntryDialog::~EntryDialog()
{
    Destroy();    
}

void EntryDialog::Create(const UINT& nResID, HINSTANCE phInstance, HWND phWndParnet, pfDialogProc pDialogProc, const E_DIALOG_STYLE& eStyle)
{
    m_lpszResName = (MAKEINTRESOURCE (nResID));
    m_hInstance = phInstance;    
    m_hWndParent = phWndParnet;    
    m_pDialogProc = pDialogProc;
    m_eStyle = eStyle;
}

void EntryDialog::Destroy()
{
    if(m_hWnd)
    {
        if(m_bDoModal)
            ::EndDialog(m_hWnd, 0);
        else
            ::DestroyWindow(m_hWnd);
    }    
    m_hWnd=NULL;
}

INT_PTR EntryDialog::DoModal()
{
    if (IsWindow(m_hWnd))
    {
        //throw CWinException(TEXT("CDialog::Create ... Window already exists"));
        return 0;
    }

    if( !m_hInstance)
    {
        return 0;
    }
    m_bDoModal=TRUE;
    EntryWIN32::SetHook( this);
    
    INT_PTR nResult=NULL;
    if( eDIALOG_TOP==m_eStyle)
        nResult=  ::DialogBox( m_hInstance, m_lpszResName, HWND_DESKTOP, m_pDialogProc);
    else
        nResult=  ::DialogBox( m_hInstance, m_lpszResName, m_hWndParent, m_pDialogProc);

    EntryWIN32::RemoveHook();
    
    return nResult;
}

HWND EntryDialog::DoModaless()
{
    if (IsWindow(m_hWnd))
    {
        CmTRACE( TEXT("CDialog::Create ... Window already exists\n"));
        return 0;
    }

    m_bDoModal=FALSE;

    EntryWIN32::SetHook(this);    
    if( eDIALOG_TOP==m_eStyle)
        m_hWnd = ::CreateDialog(m_hInstance, m_lpszResName, HWND_DESKTOP, m_pDialogProc);
    else
        m_hWnd = ::CreateDialog(m_hInstance, m_lpszResName, m_hWndParent, m_pDialogProc);
    EntryWIN32::RemoveHook();

    CmASSERT( ::IsWindow(m_hWnd) );

    if (!m_hWnd)
    {
        CmTRACE( TEXT("CDialog::Create ... Failed to create dialog\n"));
        return 0;
    }

    ::ShowWindow(m_hWnd, SW_SHOW);
    return m_hWnd;
}
/*------------------------------------------------------------------------

------------------------------------------------------------------------*/
